﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace Red.Core.Network
{
    /// <summary>
    /// HttpWebRequest wrapper. It is used to simplify setting request up.
    /// </summary>
    public class CustomWebRequest
    {
        #region Data

        private readonly Dictionary<string, string> _paramsDictionary = new Dictionary<string, string>();

        /// <summary>
        /// Used to assign a unique (per run) ID to each CustomWebRequest for the purpose of debugging
        /// </summary>
        private static int idpool = 0;
        private int id = idpool++;

        #endregion Data

        #region Public Properties

        public string Address { get; set; }
        public int Id => id;

        public string QueryString => string.Join( "&", from param in _paramsDictionary select $"{WebUtility.UrlEncode( param.Key )}={WebUtility.UrlEncode( param.Value )}" );

        public byte[] PostData => Encoding.UTF8.GetBytes( QueryString );

        #endregion Public Properties

        #region C-tor

        public CustomWebRequest()
        {
        }

        public CustomWebRequest( string address )
        {
            Address = address;
        }

        #endregion C-tor

        #region Public methods

        public void AddPostParam( string key, int value )
        {
            _paramsDictionary.Add( key, value.ToString() );
        }

        public void AddPostParam( string key, float value )
        {
            _paramsDictionary.Add( key, value.ToString() );
        }

        public void AddPostParam( string key, string value )
        {
            _paramsDictionary.Add( key, value );
        }

        public async Task<string> GetRequest()
        {
            HttpWebRequest request = CreateRequest( "GET", $"{Address}?{QueryString}" );
            request.ContentLength = 0;

            Debug.Print( $"Web request {id}: GET {request.Address.AbsoluteUri}" );

            using ( WebResponse response = await request.GetResponseAsync() )
            {
                return await RetrieveResponseContent( response );
            }
        }

        public async Task<string> PostRequest()
        {
            HttpWebRequest request = CreateRequest( "POST", Address );
            request.SendChunked = true;

            byte[] postData = PostData;
            request.ContentLength = postData.Length;
            await WriteDataToRequestStream( request, postData );

            Debug.Print( $"Web request {id}: {request.Address.AbsoluteUri} {Encoding.UTF8.GetString( postData )}" );

            using ( WebResponse response = await request.GetResponseAsync() )
            {
                return await RetrieveResponseContent( response );
            }
        }

        public async Task<string> SendRequest()
        {
            return await PostRequest();
        }

        public async Task<string> RetrieveResponseContent( WebResponse webResponse )
        {
            using ( var reader = new StreamReader( webResponse.GetResponseStream() ) )
            {
                return await reader.ReadToEndAsync();
            }
        }

        #endregion Public methods

        #region Private methods

        private HttpWebRequest CreateRequest( string method, string address )
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create( address );

            request.KeepAlive = false;
            request.Method = method;
            request.Timeout = 6000;
            request.AllowWriteStreamBuffering = true;
            request.ContentType = "application/json";
            request.UserAgent = Environment.OSVersion.ToString();

            return request;
        }

        private async Task WriteDataToRequestStream( WebRequest request, byte[] postData )
        {
            try
            {
                using ( var postStream = await request.GetRequestStreamAsync() )
                {
                    await postStream.WriteAsync( postData, 0, postData.Length );
                }
            }
            catch ( Exception e )
            {
                Console.WriteLine( $"Error writing to stream of request {id}" );
                Console.WriteLine( e.ToString() );
            }
        }

        #endregion Private methods
    }
}
